package org.chen.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUnit;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.paypal.core.PayPalHttpClient;
import com.paypal.http.HttpResponse;
import com.paypal.orders.*;
import com.paypal.payments.CapturesRefundRequest;
import com.paypal.payments.RefundRequest;
import lombok.extern.slf4j.Slf4j;
import org.chen.common.constants.Constants;
import org.chen.common.exception.ExceptionCodeEnum;
import org.chen.common.exception.FlyingException;
import org.chen.common.inter.IOSupplier;
import org.chen.common.model.SystemAdmin;
import org.chen.common.model.order.AbnormalPaymentRecord;
import org.chen.common.model.travel.StoreTravelOrder;
import org.chen.common.request.OrderRefundApplyRequest;
import org.chen.common.request.OrderTravelPayRequest;
import org.chen.common.response.OrderTravelPayResultResponse;
import org.chen.common.utils.DateUtil;
import org.chen.common.utils.RedisUtil;
import org.chen.service.*;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.mail.MessagingException;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

@Service
@Slf4j
public class OrderPayTravelServiceImpl implements OrderPayTravelService {
    @Resource
    private PayPalHttpClient payPalHttpClient;

    @Autowired
    private StoreTravelOrderService storeTravelOrderService;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private RedissonClient redisson;

    @Autowired
    private AsyncTaskService asyncTaskService;

    @Autowired
    private StoreOrderStatusService storeOrderStatusService;

    @Autowired
    private SystemConfigService systemConfigService;

    @Autowired
    private AbnormalPaymentRecordService abnormalPaymentRecordService;

    @Autowired
    private AlertService alertService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean paySuccess(StoreTravelOrder storeOrder) {
        String lockKey = "pay_success_lock:" + storeOrder.getOrderId();
        RLock lock = redisson.getLock(lockKey);

        try {
            // 如遇到没有执行完则自动延长锁
            if (!lock.tryLock(5, TimeUnit.SECONDS)) {
                log.warn("获取支付锁失败，订单号：{}", storeOrder.getOrderId());
                return false;
            }

            // 1、幂等性检查
            if (redisUtil.exists("pay_success:" + storeOrder.getOrderId())) {
                log.info("订单已处理，订单号：{}", storeOrder.getOrderId());
                return true;
            }

            // 2、验证 PayPal 支付状态（防止伪造回调）
            Order paypalOrder;
            try {
                paypalOrder = verifyPaymentStatus(storeOrder.getOrderId());
            } catch (Exception e) {
                log.error("验证PayPal支付状态失败，订单号：{}", storeOrder.getOrderId(), e);
                throw new FlyingException(ExceptionCodeEnum.PAYMENT_VERIFY_FAILED, "支付验证失败");
            }

            if (!"COMPLETED".equals(paypalOrder.status())) {
                log.warn("PayPal支付未完成，订单号：{}，状态：{}",
                        storeOrder.getOrderId(), paypalOrder.status());
                return false;
            }
            // 3、获取实际支付金额
            BigDecimal paidAmount = extractPaidAmount(paypalOrder);
            log.info("订单号：{}，PayPal实际支付金额：{}", storeOrder.getOrderId(), paidAmount);

            // 4、查询订单（行锁）
            StoreTravelOrder existingOrder = storeTravelOrderService.getByPayIdWithLock(storeOrder.getOrderId());
            //  4.1 订单不存在，自动退款
            if (existingOrder == null) {
                log.error("订单不存在，但收到支付成功回调，执行自动退款，订单号：{}",
                        storeOrder.getOrderId());

                // 创建退款记录（用于追踪）
                saveRefundRecord(storeOrder.getOrderId(), paidAmount, "订单不存在");

                // 异步执行退款
                asyncTaskService.autoRefundForNonExistentOrder(storeOrder.getOrderId(), paidAmount);

                // 发送告警
                alertService.sendAlert(
                        "异常支付-订单不存在",
                        String.format("PayPal订单：%s，金额：%s，已自动退款",
                                storeOrder.getOrderId(), paidAmount)
                );

                return false;

            }

            //  4.2 订单已支付，检查是否重复支付
            if (existingOrder.getPaid()) {
                log.warn("订单已支付，疑似重复支付，订单号：{}，原支付单号：{}，新支付单号：{}",
                        existingOrder.getOrderId(), existingOrder.getPayId(), storeOrder.getOrderId());
                // 如果是不同的 PayPal 订单号，说明是重复支付
                if (!existingOrder.getPayId().equals(storeOrder.getOrderId())) {
                    log.error("检测到重复支付，订单号：{}，原支付单：{}，新支付单：{}",
                            existingOrder.getOrderId(), existingOrder.getPayId(), storeOrder.getOrderId());

                    // 创建退款记录
                    saveRefundRecord(storeOrder.getOrderId(), paidAmount, "重复支付");

                    // 异步执行退款
                    asyncTaskService.autoRefundForDuplicatePayment(
                            existingOrder, storeOrder.getOrderId(), paidAmount);

                    // 发送告警
                    alertService.sendAlert(
                            "异常支付-重复支付",
                            String.format("订单号：%s，重复支付单号：%s，金额：%s，已自动退款",
                                    existingOrder.getOrderId(), storeOrder.getOrderId(), paidAmount)
                    );

                    return false;
                }

                // 同一个支付单，幂等返回
                log.info("订单已支付（幂等），订单号：{}", existingOrder.getOrderId());
                return true;
            }

            //  4.3 金额校验
            if (paidAmount.compareTo(existingOrder.getPayPrice()) != 0) {
                log.error("支付金额不匹配，订单号：{}，订单金额：{}，实际支付：{}",
                        existingOrder.getOrderId(), existingOrder.getPayPrice(), paidAmount);

                // 金额不一致，自动退款
                saveRefundRecord(storeOrder.getOrderId(), paidAmount,
                        String.format("金额不匹配，订单金额：%s，实际支付：%s",
                                existingOrder.getPayPrice(), paidAmount));

                asyncTaskService.autoRefundForAmountMismatch(
                        existingOrder, storeOrder.getOrderId(), paidAmount);

                alertService.sendAlert(
                        "异常支付-金额不匹配",
                        String.format("订单号：%s，订单金额：%s，实际支付：%s，已自动退款",
                                existingOrder.getOrderId(), existingOrder.getPayPrice(), paidAmount)
                );

                return false;
            }

            //  4.4 订单已退款，不能再次支付
            if (existingOrder.getRefundStatus() == 2) {
                log.error("订单已退款，但收到新的支付回调，订单号：{}", existingOrder.getOrderId());

                saveRefundRecord(storeOrder.getOrderId(), paidAmount, "订单已退款");

                asyncTaskService.autoRefundForRefundedOrder(
                        existingOrder, storeOrder.getOrderId(), paidAmount);

                alertService.sendAlert(
                        "异常支付-订单已退款",
                        String.format("订单号：%s，已退款但收到新支付，金额：%s，已自动退款",
                                existingOrder.getOrderId(), paidAmount)
                );

                return false;
            }

            // 4.5 订单已取消但可以恢复
            if (existingOrder.getStatus() == 3 ||
                    existingOrder.getIsDel() ||
                    existingOrder.getIsSystemDel()) {

                log.warn("订单已取消，但收到支付成功回调，尝试恢复订单，订单号：{}",
                        existingOrder.getOrderId());

                // 检查是否允许恢复（可配置）
                boolean canRecover = Boolean.parseBoolean(checkIfOrderCanBeRecovered(existingOrder));

                if (!canRecover) {
                    log.error("订单不允许恢复，执行自动退款，订单号：{}", existingOrder.getOrderId());

                    saveRefundRecord(storeOrder.getOrderId(), paidAmount, "订单已取消且不可恢复");

                    asyncTaskService.autoRefundForCancelledOrder(
                            existingOrder, storeOrder.getOrderId(), paidAmount);

                    alertService.sendAlert(
                            "异常支付-订单不可恢复",
                            String.format("订单号：%s，已取消且不可恢复，金额：%s，已自动退款",
                                    existingOrder.getOrderId(), paidAmount)
                    );

                    return false;
                }

                // 恢复订单
                boolean updateResult = recoverOrder(existingOrder, storeOrder.getOrderId(), paidAmount);

                if (!updateResult) {
                    log.error("订单恢复失败，执行自动退款，订单号：{}", existingOrder.getOrderId());

                    saveRefundRecord(storeOrder.getOrderId(), paidAmount, "订单恢复失败");

                    asyncTaskService.autoRefundForRecoveryFailed(
                            existingOrder, storeOrder.getOrderId(), paidAmount);

                    return false;
                }

                // 记录特殊日志
                storeOrderStatusService.createTravelLog(
                        existingOrder.getId(),
                        "order_recovered",
                        "订单已取消但收到支付成功回调，已自动恢复"
                );

                log.info("订单恢复成功，订单号：{}", existingOrder.getOrderId());

            } else {
                // 9️⃣ 【正常流程】更新订单状态
                boolean updateResult = updateOrderToPaid(existingOrder, storeOrder.getOrderId(), paidAmount);

                if (!updateResult) {
                    throw new FlyingException(ExceptionCodeEnum.ORDER_UPDATE_FAILED, "订单状态更新失败");
                }
            }

            // 5、标记订单已处理（幂等性）
            redisUtil.set("pay_success:" + storeOrder.getOrderId(), "1", Constants.TOKEN_EXPRESS_MINUTES_1, TimeUnit.MINUTES);

            // 6、从取消队列移除
            redisUtil.zRem("order_cancel_delay", existingOrder.getOrderId());

            log.info("订单支付成功，已从取消队列移除，订单号：{}", existingOrder.getOrderId());

            // 7、异步处理后续操作
            asyncTaskService.processSuccessfulPayment(existingOrder);

            return true;
        } catch (Exception e) {
            log.error("Error processing payment success for order: {}", storeOrder.getOrderId(), e);
            throw new FlyingException(ExceptionCodeEnum.PAYMENT_PROCESSING_ERROR, "支付处理出错: " + e.getMessage());
        } finally {
            if (lock.isHeldByCurrentThread()){
                lock.unlock();
            }
        }
    }

    /**
     * 从 PayPal 订单中提取实际支付金额
     */
    private BigDecimal extractPaidAmount(Order paypalOrder) {
        try {
            String amountStr = paypalOrder.purchaseUnits().get(0)
                    .amountWithBreakdown().value();
            return new BigDecimal(amountStr);
        } catch (Exception e) {
            log.error("提取支付金额失败", e);
            throw new FlyingException(ExceptionCodeEnum.PAYMENT_AMOUNT_EXTRACT_FAILED,
                    "提取支付金额失败");
        }
    }

    /**
     * 检查订单是否可以恢复
     */
    private String checkIfOrderCanBeRecovered(StoreTravelOrder order) {
        // 检查配置（是否允许恢复已取消订单）
        return systemConfigService.getValueByKey("allow_recover_cancelled_order");
    }

    /**
     * 恢复订单
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean recoverOrder(StoreTravelOrder order, String payPalOrderId, BigDecimal paidAmount) {
        order.setStatus(1);           // 改为待使用
        order.setPaid(true);          // 标记为已支付
        order.setIsDel(false);        // 取消删除标记
        order.setIsSystemDel(false);
        order.setPayId(payPalOrderId); // 更新支付单号
        order.setPayTime(DateUtil.nowDateTime());
        order.setPayPrice(paidAmount); // 更新实际支付金额
        order.setUpdateTime(DateUtil.nowDateTime());

        return storeTravelOrderService.updateById(order);
    }

    /**
     * 更新订单为已支付
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean updateOrderToPaid(StoreTravelOrder order, String payPalOrderId, BigDecimal paidAmount) {
        order.setStatus(1);
        order.setPaid(true);
        order.setPayId(payPalOrderId);
        order.setPayTime(DateUtil.nowDateTime());
        order.setPayPrice(paidAmount);
        order.setUpdateTime(DateUtil.nowDateTime());

        return storeTravelOrderService.updateById(order);
    }

    /**
     * 保存异常支付退款记录（用于审计）
     */
    @Transactional(rollbackFor = Exception.class)
    public void saveRefundRecord(String payPalOrderId, BigDecimal amount, String reason) {
        AbnormalPaymentRecord record = new AbnormalPaymentRecord();
        record.setPayPalOrderId(payPalOrderId);
        record.setAmount(amount);
        record.setReason(reason);
        record.setStatus("PENDING"); // 待退款
        record.setCreateTime(new Date());

        abnormalPaymentRecordService.save(record);

        log.info("异常支付记录已保存，PayPal订单号：{}，金额：{}，原因：{}",
                payPalOrderId, amount, reason);
    }

    @Retryable(value = {IOException.class}, backoff = @Backoff(delay = 1000))
    private Order verifyPaymentStatus(String orderId) throws IOException {
        return payOrder(orderId);
    }

    private OrderRequest createOrderRequest(OrderTravelPayRequest orderPayRequest, StoreTravelOrder order) {
        OrderRequest orderRequest = new OrderRequest();
        orderRequest.checkoutPaymentIntent("CAPTURE");

        ApplicationContext applicationContext = new ApplicationContext()
                .brandName("成都浪腾广告有限公司")
                .landingPage("BILLING")
                .userAction("PAY_NOW")
                .returnUrl("http://192.168.1.12:8082/#/about")
                .cancelUrl("http://192.168.1.12:8082/#/cancel");
        orderRequest.applicationContext(applicationContext);

        List<PurchaseUnitRequest> purchaseUnitRequests = new ArrayList<>();
        PurchaseUnitRequest purchaseUnitRequest = new PurchaseUnitRequest()
                .referenceId(order.getOrderId())
                .description("Travel Order")
                .customId(orderPayRequest.getOrderNo())
                .amountWithBreakdown(new AmountWithBreakdown().currencyCode("THB").value(order.getPayPrice().toString()));

        purchaseUnitRequests.add(purchaseUnitRequest);
        orderRequest.purchaseUnits(purchaseUnitRequests);

        return orderRequest;
    }

    private OrderTravelPayResultResponse createPayResultResponse(Order paypalOrder) {
        OrderTravelPayResultResponse resultResponse = new OrderTravelPayResultResponse();
        resultResponse.setOrderNo(paypalOrder.id());
        resultResponse.setStatus(paypalOrder.status());
        String approvalLink = paypalOrder.links().stream()
                .filter(link -> "approve".equals(link.rel()))
                .findFirst()
                .map(LinkDescription::href)
                .orElse(null);
        resultResponse.setUrl(approvalLink);
        return resultResponse;
    }

    private void updateOrderWithPaymentInfo(StoreTravelOrder order, Order paypalOrder, String approvalLink) {
        order.setPayId(paypalOrder.id());
        order.setPayUrl(approvalLink);
        boolean updateSuccess = storeTravelOrderService.updateById(order);
        if (!updateSuccess) {
            throw new FlyingException(ExceptionCodeEnum.PAYMENT_LINK_GENERATION_FAILED, "支付链接生成失败！");
        }
    }

    private Order executeWithRetry(IOSupplier<Order> supplier) throws IOException {
        int maxRetries = 3;
        IOException lastException = null;

        for (int i = 0; i < maxRetries; i++) {
            try {
                return supplier.get();
            } catch (IOException e) {
                lastException = e;
                log.warn("Attempt {} failed: {}", i + 1, e.getMessage());
                if (i < maxRetries - 1) {
                    try {
                        Thread.sleep(1000 * (i + 1));
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        throw new IOException("Interrupted during retry", ie);
                    }
                }
            }
        }

        log.error("All {} attempts failed", maxRetries);
        throw new IOException("Failed after " + maxRetries + " retries", lastException);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public OrderTravelPayResultResponse payment(OrderTravelPayRequest orderPayRequest, String ip) {
        String lockKey = "order_payment_lock:" + orderPayRequest.getOrderNo();
        RLock lock = redisson.getLock(lockKey);
        try {
            // 尝试获取锁，等待5秒后自动释放，如遇到没有执行完则自动延长锁
            if (!lock.tryLock(5, TimeUnit.SECONDS)) {
                throw new FlyingException(ExceptionCodeEnum.SYSTEM_BUSY, "系统繁忙，请稍后重试");
            }

            // 幂等性检查
            if (redisUtil.exists("payment_processed:" + orderPayRequest.getOrderNo())) {
                throw new FlyingException(ExceptionCodeEnum.PAYMENT_ALREADY_PROCESSED, "该订单已生成支付链接,如需重新生成请1小时后再试");
            }

            StoreTravelOrder order = storeTravelOrderService.getByOrderNoWithLock(orderPayRequest.getOrderNo());

            validateOrder(order);

            OrderRequest orderRequest = createOrderRequest(orderPayRequest, order);
            OrdersCreateRequest request = new OrdersCreateRequest().requestBody(orderRequest);

            Order paypalOrder = executeWithRetry(() -> payPalHttpClient.execute(request).result());

            OrderTravelPayResultResponse resultResponse = createPayResultResponse(paypalOrder);

            updateOrderWithPaymentInfo(order, paypalOrder, resultResponse.getUrl());

            // 异步处理非关键操作
            asyncTaskService.processPaymentAsync(order);

            // 标记订单已处理
            redisUtil.set("payment_processed:" + orderPayRequest.getOrderNo(), "1", Constants.TOKEN_EXPRESS_MINUTES_1, TimeUnit.MINUTES);

            return resultResponse;
        }catch (IOException | InterruptedException e) {
            log.error("PayPal API error", e);
            throw new FlyingException(ExceptionCodeEnum.PAYMENT_PROCESSING_ERROR, "支付处理出错");
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    private void validateOrder(StoreTravelOrder order) {
        if (order == null){
            throw new FlyingException(ExceptionCodeEnum.NOT_FOUND,ExceptionCodeEnum.NOT_FOUND.getMessage());
        }
        if (order.getIsDel() || order.getIsSystemDel()) {
            throw new FlyingException(ExceptionCodeEnum.THE_ORDER_HAS_EXPIRED_PLEASE_PLACE_A_NEW_ORDER, "订单已失效，请重新下单！");
        }
        if (order.getPaid()) {
            throw new FlyingException(ExceptionCodeEnum.THE_PAYMENT_HAS_BEEN_COMPLETED, "您的付款已完成。");
        }
        if (order.getPayId() != null && !order.getPayId().isEmpty()) {
            throw new FlyingException(ExceptionCodeEnum.A_PAYMENT_ORDER_NUMBER_HAS_BEEN_GENERATED, order.getPayId());
        }
    }

    @Override
    public String queryPayResult(String orderNo) throws IOException{
        StoreTravelOrder one = storeTravelOrderService.getOne(new QueryWrapper<StoreTravelOrder>().eq("order_id", orderNo).or().eq("pay_id", orderNo));
        if (one != null){
            if (one.getPayId() != null){
                if (one.getPaid()){
                    return "COMPLETED";
                }else {
                    orderNo = one.getPayId();
                }
            }else {
                return "No";
            }
        }
        OrdersGetRequest request = new OrdersGetRequest(orderNo);
        HttpResponse<Order> response = payPalHttpClient.execute(request);
        return response.result().status();
    }

    private Order payOrder(String orderId) throws IOException {
        OrdersCaptureRequest captureRequest = new OrdersCaptureRequest(orderId);
        HttpResponse<Order> response = payPalHttpClient.execute(captureRequest);
        return response.result();
    }

}
